"""
配置管理器模块
Configuration Manager Module
"""

from typing import Dict, List, Any, Optional
import yaml
import os
from datetime import datetime, timedelta


class ConfigValidationError(Exception):
    """配置验证异常"""
    pass


class ConfigManager:
    """配置管理器类"""
    
    # 业态类型映射
    BUSINESS_TYPES = {
        'catering': {'code': 'hy-1', 'name': '餐饮'},
        'entertainment': {'code': 'hy-4', 'name': '娱乐'},
        'retail': {'code': 'hy-5', 'name': '零售'}
    }
    
    # 支持的城市列表
    CITIES = ['sz', 'gz', 'bj', 'sh']  # 深圳、广州、北京、上海
    
    # 默认请求设置
    DEFAULT_REQUEST_SETTINGS = {
        'delay': 2,  # 请求间隔秒数
        'timeout': 30,  # 请求超时时间
        'retry_times': 3,  # 重试次数
        'headers': {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
    }
    
    def __init__(self, config_file: str = None):
        """初始化配置管理器"""
        self.config_file = config_file
        self.config = self._load_default_config()
        
        if config_file and os.path.exists(config_file):
            self._load_config_file()
        
        # 验证配置
        self._validate_config()
    
    def _load_default_config(self) -> Dict[str, Any]:
        """加载默认配置"""
        return {
            'scraper': {
                'cities': ['sz'],
                'business_types': ['catering', 'entertainment', 'retail'],
                'date_range_days': 30
            },
            'request': self.DEFAULT_REQUEST_SETTINGS.copy(),
            'storage': {
                'format': 'json',
                'output_dir': './data',
                'enable_dedup': True
            },
            'logging': {
                'level': 'INFO',
                'file': './logs/scraper.log',
                'console': True
            }
        }
    
    def _load_config_file(self):
        """从文件加载配置"""
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                file_config = yaml.safe_load(f)
                self._merge_config(file_config)
        except Exception as e:
            print(f"Warning: Failed to load config file {self.config_file}: {e}")
    
    def _merge_config(self, file_config: Dict[str, Any]):
        """合并配置"""
        for key, value in file_config.items():
            if key in self.config and isinstance(self.config[key], dict) and isinstance(value, dict):
                self.config[key].update(value)
            else:
                self.config[key] = value
    
    def get(self, key: str, default=None):
        """获取配置值"""
        keys = key.split('.')
        value = self.config
        
        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default
        
        return value
    
    def get_business_type_code(self, business_type: str) -> str:
        """获取业态代码"""
        return self.BUSINESS_TYPES.get(business_type, {}).get('code', '')
    
    def get_business_type_name(self, business_type: str) -> str:
        """获取业态名称"""
        return self.BUSINESS_TYPES.get(business_type, {}).get('name', '')
    
    def _validate_config(self):
        """验证配置参数"""
        self._validate_scraper_config()
        self._validate_request_config()
        self._validate_storage_config()
        self._validate_logging_config()
    
    def _validate_scraper_config(self):
        """验证爬虫配置"""
        scraper_config = self.config.get('scraper', {})
        
        # 验证城市列表
        cities = scraper_config.get('cities', [])
        if not isinstance(cities, list) or not cities:
            raise ConfigValidationError("cities must be a non-empty list")
        
        for city in cities:
            if city not in self.CITIES:
                raise ConfigValidationError(f"Unsupported city: {city}. Supported cities: {self.CITIES}")
        
        # 验证业态类型
        business_types = scraper_config.get('business_types', [])
        if not isinstance(business_types, list) or not business_types:
            raise ConfigValidationError("business_types must be a non-empty list")
        
        for bt in business_types:
            if bt not in self.BUSINESS_TYPES:
                raise ConfigValidationError(f"Unsupported business type: {bt}. Supported types: {list(self.BUSINESS_TYPES.keys())}")
        
        # 验证日期范围
        date_range_days = scraper_config.get('date_range_days', 30)
        if not isinstance(date_range_days, int) or date_range_days <= 0:
            raise ConfigValidationError("date_range_days must be a positive integer")
        
        if date_range_days > 365:
            raise ConfigValidationError("date_range_days cannot exceed 365 days")
    
    def _validate_request_config(self):
        """验证请求配置"""
        request_config = self.config.get('request', {})
        
        # 验证延迟时间
        delay = request_config.get('delay', 2)
        if not isinstance(delay, (int, float)) or delay < 1 or delay > 10:
            raise ConfigValidationError("request delay must be between 1 and 10 seconds")
        
        # 验证超时时间
        timeout = request_config.get('timeout', 30)
        if not isinstance(timeout, (int, float)) or timeout <= 0:
            raise ConfigValidationError("request timeout must be a positive number")
        
        # 验证重试次数
        retry_times = request_config.get('retry_times', 3)
        if not isinstance(retry_times, int) or retry_times < 0 or retry_times > 10:
            raise ConfigValidationError("retry_times must be between 0 and 10")
        
        # 验证请求头
        headers = request_config.get('headers', {})
        if not isinstance(headers, dict):
            raise ConfigValidationError("headers must be a dictionary")
        
        if 'User-Agent' not in headers:
            raise ConfigValidationError("User-Agent header is required")
    
    def _validate_storage_config(self):
        """验证存储配置"""
        storage_config = self.config.get('storage', {})
        
        # 验证输出格式
        format_type = storage_config.get('format', 'json')
        if format_type not in ['json', 'csv']:
            raise ConfigValidationError("storage format must be 'json' or 'csv'")
        
        # 验证输出目录
        output_dir = storage_config.get('output_dir', './data')
        if not isinstance(output_dir, str) or not output_dir.strip():
            raise ConfigValidationError("output_dir must be a non-empty string")
        
        # 验证去重设置
        enable_dedup = storage_config.get('enable_dedup', True)
        if not isinstance(enable_dedup, bool):
            raise ConfigValidationError("enable_dedup must be a boolean")
    
    def _validate_logging_config(self):
        """验证日志配置"""
        logging_config = self.config.get('logging', {})
        
        # 验证日志级别
        level = logging_config.get('level', 'INFO')
        valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
        if level not in valid_levels:
            raise ConfigValidationError(f"logging level must be one of: {valid_levels}")
        
        # 验证日志文件路径
        log_file = logging_config.get('file', './logs/scraper.log')
        if not isinstance(log_file, str) or not log_file.strip():
            raise ConfigValidationError("logging file must be a non-empty string")
    
    def get_cities(self) -> List[str]:
        """获取城市列表"""
        return self.config.get('scraper', {}).get('cities', ['sz'])
    
    def get_business_types(self) -> List[str]:
        """获取业态类型列表"""
        return self.config.get('scraper', {}).get('business_types', ['catering', 'entertainment', 'retail'])
    
    def get_date_range_days(self) -> int:
        """获取日期范围天数"""
        return self.config.get('scraper', {}).get('date_range_days', 30)
    
    def get_request_delay(self) -> float:
        """获取请求延迟时间"""
        return self.config.get('request', {}).get('delay', 2)
    
    def get_request_timeout(self) -> int:
        """获取请求超时时间"""
        return self.config.get('request', {}).get('timeout', 30)
    
    def get_retry_times(self) -> int:
        """获取重试次数"""
        return self.config.get('request', {}).get('retry_times', 3)
    
    def get_headers(self) -> Dict[str, str]:
        """获取请求头"""
        return self.config.get('request', {}).get('headers', self.DEFAULT_REQUEST_SETTINGS['headers'])
    
    def get_storage_format(self) -> str:
        """获取存储格式"""
        return self.config.get('storage', {}).get('format', 'json')
    
    def get_output_dir(self) -> str:
        """获取输出目录"""
        return self.config.get('storage', {}).get('output_dir', './data')
    
    def is_dedup_enabled(self) -> bool:
        """检查是否启用去重"""
        return self.config.get('storage', {}).get('enable_dedup', True)
    
    def get_log_level(self) -> str:
        """获取日志级别"""
        return self.config.get('logging', {}).get('level', 'INFO')
    
    def get_log_file(self) -> str:
        """获取日志文件路径"""
        return self.config.get('logging', {}).get('file', './logs/scraper.log')
    
    def is_console_logging_enabled(self) -> bool:
        """检查是否启用控制台日志"""
        return self.config.get('logging', {}).get('console', True)
    
    def get_all_business_type_codes(self) -> List[str]:
        """获取所有配置的业态代码"""
        business_types = self.get_business_types()
        return [self.get_business_type_code(bt) for bt in business_types if self.get_business_type_code(bt)]
    
    def validate_business_type(self, business_type: str) -> bool:
        """验证业态类型是否有效"""
        return business_type in self.BUSINESS_TYPES
    
    def validate_city(self, city: str) -> bool:
        """验证城市是否有效"""
        return city in self.CITIES
    
    def to_dict(self) -> Dict[str, Any]:
        """返回配置字典"""
        return self.config.copy()
    
    def save_config(self, file_path: str = None):
        """保存配置到文件"""
        if file_path is None:
            file_path = self.config_file or 'config.yaml'
        
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                yaml.dump(self.config, f, default_flow_style=False, allow_unicode=True, indent=2)
        except Exception as e:
            raise ConfigValidationError(f"Failed to save config to {file_path}: {e}")